package com.jprocms.module.cms.controller.portal.api;

import com.jpro.framework.common.pojo.CommonResult;
import com.jpro.framework.ip.core.Area;
import com.jpro.framework.ip.core.utils.IPUtils;
import com.jpro.framework.security.core.util.RequestUtils;
import com.jpro.framework.security.core.util.SecurityFrameworkUtils;
import com.jprocms.module.cms.api.dto.SiteConfigBaseDto;
import com.jprocms.module.cms.controller.portal.vo.SiteFlowVO;
import com.jprocms.module.cms.dal.dataobject.accesslog.AccessLogDO;
import com.jprocms.module.cms.dal.dataobject.accessstatistics.AccessStatisticsDO;
import com.jprocms.module.cms.dal.dataobject.site.SiteDO;
import com.jprocms.module.cms.dal.redis.RedisKeyConstants;
import com.jprocms.module.cms.framework.cache.CacheService;
import com.jprocms.module.cms.service.accesslog.AccessService;
import com.jprocms.module.cms.service.content.ContentCountService;
import com.jprocms.module.cms.service.site.SiteService;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.UserAgent;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import static com.jprocms.module.cms.constant.CmsSysConstants.*;
import static com.jprocms.module.cms.dal.dataobject.accessstatistics.AccessStatisticsDO.SEARCH_ENGINE_LIST;


/**
 * 访问统计 接口
 *
 * @author jprocms
 */
@Slf4j
@Tag(name = "访问统计", description = "访问统计")
@RestController
@RequestMapping({FRONT_API + "/flow"})
public class SiteFlowApiController {

    @Resource
    private SiteService siteService;
    @Resource
    private CacheService cacheService;
    @Resource
    private ContentCountService contentCountService;
    @Resource
    private AccessService accessService;

    @Operation(summary = "记录前台访问接口")
    @RequestMapping(value = "{siteId:[\\d]+}", method = {RequestMethod.POST, RequestMethod.GET})
    public CommonResult<SiteFlowVO> siteFlow(@Parameter(description = "站点ID") @PathVariable Long siteId,
                                             @Parameter(description = "受访URL地址") String url,
                                             @Parameter(description = "入口地址,第一个打开的页面即为入口页面") String entryUrl,
                                             @Parameter(description = "来源页面,当前页面的上一个页面") String referrer,
                                             @Parameter(description = "会话ID(Session ID") Long sessionId,
                                             @Parameter(description = "访客ID(Unique Visitor)") Long uv,
                                             @Parameter(description = "是否新访客。如果用户是第一次访问网站，本次会话的所有访问都应为新访客访问") Boolean newVisitor,
                                             @Parameter(description = "访问计数。正常访问为1 ，关闭或隐藏页面结束访问为0 ") Integer count,
                                             @Parameter(description = "持续时间,本次访问会话的持续时间，从本次会话访问的第一个页面开始计算时间。单位：秒") Integer duration,
                                             HttpServletRequest request) {
        final SiteDO site = siteService.getSite(siteId);
        SiteFlowVO siteFlowVO = new SiteFlowVO();
        if (site == null) {
            return CommonResult.success(siteFlowVO);
        }
        siteFlowVO.setGreyOpen(site.getSiteConfig().getBase().getGreyOpen());
        String host = UriComponentsBuilder.fromHttpUrl(url).build().getHost();
        String source = AccessStatisticsDO.SOURCE_DIRECT;
        String sourceType = AccessStatisticsDO.SOURCE_DIRECT;
        if (StringUtils.isNotBlank(referrer)) {
            UriComponents uriComp = UriComponentsBuilder.fromHttpUrl(referrer).build();
            String referHost = StringUtils.lowerCase(uriComp.getHost());
            if (StringUtils.equals(host, referHost)) {
                source = AccessStatisticsDO.SOURCE_INNER;
                sourceType = AccessStatisticsDO.SOURCE_INNER;
            } else {
                StringBuilder sourceBuff = new StringBuilder(uriComp.getScheme() + "://" + referHost);
                if (uriComp.getPort() != -1) {
                    sourceBuff.append(":").append(uriComp.getPort());
                }
                source = sourceBuff.toString();
                if (SEARCH_ENGINE_LIST.contains(referHost)) {
                    sourceType = AccessStatisticsDO.SOURCE_SEARCH;
                } else {
                    sourceType = AccessStatisticsDO.SOURCE_OUTER;
                }
            }
        }

        String userAgentStr = request.getHeader("user-agent");
        UserAgent userAgent = UserAgent.parseUserAgentString(userAgentStr);
        Browser agentBrowser = userAgent.getBrowser();
        // 不统计机器人访问的数据
        if (agentBrowser.equals(Browser.BOT) || (agentBrowser.equals(Browser.BOT_MOBILE))) {
            return CommonResult.success(siteFlowVO);
        }

        String browser = agentBrowser.toString();
        String ip = RequestUtils.getRemoteAddr(request);
        String os = userAgent.getOperatingSystem().toString();
        String device = userAgent.getOperatingSystem().getDeviceType().toString();
        final Area area = IPUtils.getArea(ip);

        AccessLogDO accessLogDO = new AccessLogDO();
        accessLogDO.setSiteId(siteId);
        final Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        if (loginUserId != null) {
            accessLogDO.setUserId(loginUserId);
        } else {
            accessLogDO.setUserId(ANONYMOUS_USER_ID);
        }
        accessLogDO.setAccessUrl(StringUtils.substring(StringUtils.substringBefore(url, "?"), 0, 255));
        accessLogDO.setEntryUrl(StringUtils.substring(StringUtils.substringBefore(entryUrl, "?"), 0, 255));
        accessLogDO.setAccessSource(source);
        accessLogDO.setSourceType(sourceType);
        accessLogDO.setAccessCountry(IPUtils.getCountry(area));
        accessLogDO.setAccessProvince(IPUtils.getProvice(area));
        accessLogDO.setAccessDevice(device);
        accessLogDO.setAccessOs(os);
        accessLogDO.setAccessBrowser(browser);
        accessLogDO.setAccessUserAgent(StringUtils.substring(userAgentStr, 0, 255));
        accessLogDO.setAccessCount(count);
        accessLogDO.setAccessSessionId(sessionId);
        accessLogDO.setAccessUv(uv);
        accessLogDO.setAccessIp(ip);
        accessLogDO.setAccessDuration(duration);
        accessLogDO.setNewVisitor(newVisitor);
        accessLogDO.setTenantId(CMS_TENANT_ID);
        accessService.cacheAccessLog(accessLogDO);

        return CommonResult.success(siteFlowVO);
    }

    @Operation(summary = "获取站点访问统计数据")
    @GetMapping("/view/{siteId:[\\d]+}")
    public CommonResult<SiteFlowVO> getSiteView(@Parameter(description = "站点ID") @PathVariable Long siteId, HttpServletRequest request) {
        final SiteDO site = siteService.getSite(siteId);
        SiteFlowVO siteFlowVO = new SiteFlowVO();
        if (site != null) {
            final SiteConfigBaseDto siteConfigBase = site.getSiteConfig().getBase();
            siteFlowVO.setOnlyIndexGrey(siteConfigBase.getGreyOpen() && siteConfigBase.getOnlyIndexGrey());
            siteFlowVO.setGreyOpen(siteConfigBase.getGreyOpen());
            siteFlowVO.setViews(Long.parseLong(getCacheView(RedisKeyConstants.SITE_VIEW, siteId.toString(), site.getViews()).toString()));
            siteFlowVO.setTodayViews(Integer.parseInt(getCacheView(RedisKeyConstants.SITE_DAY_VIEW, siteId.toString(), site.getTodayViews()).toString()));
            siteFlowVO.setYearViews(Long.parseLong(getCacheView(RedisKeyConstants.SITE_YEAR_VIEW, siteId.toString(), site.getYearViews()).toString()));
            siteFlowVO.setYesterdayViews(site.getYesterdayViews());
            siteFlowVO.setOnlineUvCount(site.getUvCount());
        }
        return CommonResult.success(siteFlowVO);
    }

    @Operation(summary = "获取站点访问统计数据")
    @GetMapping("/index/{siteId:[\\d]+}")
    public CommonResult<SiteFlowVO> viewIndex(@Parameter(description = "站点ID") @PathVariable Long siteId, HttpServletRequest request) {
        contentCountService.viewIndex(siteId);
        final SiteDO site = siteService.getSite(siteId);
        SiteFlowVO siteFlowVO = new SiteFlowVO();
        if (site != null) {
            final SiteConfigBaseDto siteConfigBase = site.getSiteConfig().getBase();
            siteFlowVO.setOnlyIndexGrey(siteConfigBase.getGreyOpen() && siteConfigBase.getOnlyIndexGrey());
        }
        return CommonResult.success(siteFlowVO);
    }

    private Object getCacheView(String key, String id, Object dbval) {
        Object cacheViewCount = cacheService.getCacheMapValue(key, id);
        if (cacheViewCount != null) {
            return cacheViewCount;
        }
        return dbval;
    }

}
